use actix_web::{HttpResponse, Result};
use serde::{Deserialize, Serialize};
use chrono::{DateTime, Utc};
use uuid::Uuid;

#[derive(Serialize, Deserialize, Debug)]
pub struct ApiResponse<T> {
    pub success: bool,
    pub data: Option<T>,
    pub message: Option<String>,
    pub timestamp: DateTime<Utc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub request_id: Option<Uuid>,
}

impl<T> ApiResponse<T> {
    pub fn success(data: T) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: None,
            timestamp: Utc::now(),
            request_id: None,
        }
    }

    pub fn success_with_message(data: T, message: &str) -> Self {
        Self {
            success: true,
            data: Some(data),
            message: Some(message.to_string()),
            timestamp: Utc::now(),
            request_id: None,
        }
    }

    pub fn error(message: &str) -> ApiResponse<()> {
        ApiResponse {
            success: false,
            data: None,
            message: Some(message.to_string()),
            timestamp: Utc::now(),
            request_id: None,
        }
    }

    pub fn with_request_id(mut self, request_id: Uuid) -> Self {
        self.request_id = Some(request_id);
        self
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PaginatedResponse<T> {
    pub items: Vec<T>,
    pub pagination: PaginationMeta,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct PaginationMeta {
    pub current_page: u64,
    pub per_page: u64,
    pub total_items: u64,
    pub total_pages: u64,
    pub has_next: bool,
    pub has_prev: bool,
}

impl PaginationMeta {
    pub fn new(current_page: u64, per_page: u64, total_items: u64) -> Self {
        let total_pages = (total_items + per_page - 1) / per_page; // Ceiling division
        
        Self {
            current_page,
            per_page,
            total_items,
            total_pages,
            has_next: current_page < total_pages,
            has_prev: current_page > 1,
        }
    }
}

impl<T> PaginatedResponse<T> {
    pub fn new(items: Vec<T>, current_page: u64, per_page: u64, total_items: u64) -> Self {
        Self {
            items,
            pagination: PaginationMeta::new(current_page, per_page, total_items),
        }
    }
}

// Helper functions for common HTTP responses
pub fn ok<T: Serialize>(data: T) -> Result<HttpResponse> {
    Ok(HttpResponse::Ok().json(ApiResponse::success(data)))
}

pub fn ok_with_message<T: Serialize>(data: T, message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::Ok().json(ApiResponse::success_with_message(data, message)))
}

pub fn created<T: Serialize>(data: T) -> Result<HttpResponse> {
    Ok(HttpResponse::Created().json(ApiResponse::success(data)))
}

pub fn created_with_message<T: Serialize>(data: T, message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::Created().json(ApiResponse::success_with_message(data, message)))
}

pub fn no_content() -> Result<HttpResponse> {
    Ok(HttpResponse::NoContent().finish())
}

pub fn bad_request(message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::BadRequest().json(ApiResponse::<()>::error(message)))
}

pub fn unauthorized(message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::Unauthorized().json(ApiResponse::<()>::error(message)))
}

pub fn forbidden(message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::Forbidden().json(ApiResponse::<()>::error(message)))
}

pub fn not_found(message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::NotFound().json(ApiResponse::<()>::error(message)))
}

pub fn internal_error(message: &str) -> Result<HttpResponse> {
    Ok(HttpResponse::InternalServerError().json(ApiResponse::<()>::error(message)))
}

pub fn paginated<T: Serialize>(
    items: Vec<T>,
    current_page: u64,
    per_page: u64,
    total_items: u64,
) -> Result<HttpResponse> {
    let response = PaginatedResponse::new(items, current_page, per_page, total_items);
    Ok(HttpResponse::Ok().json(ApiResponse::success(response)))
}

#[derive(Serialize, Deserialize, Debug)]
pub struct HealthStatus {
    pub status: String,
    pub timestamp: DateTime<Utc>,
    pub version: String,
    pub uptime: String,
    pub services: Vec<ServiceHealth>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ServiceHealth {
    pub name: String,
    pub status: String,
    pub response_time_ms: Option<u64>,
    pub details: Option<serde_json::Value>,
}

impl ServiceHealth {
    pub fn healthy(name: &str, response_time_ms: u64) -> Self {
        Self {
            name: name.to_string(),
            status: "healthy".to_string(),
            response_time_ms: Some(response_time_ms),
            details: None,
        }
    }

    pub fn unhealthy(name: &str, details: serde_json::Value) -> Self {
        Self {
            name: name.to_string(),
            status: "unhealthy".to_string(),
            response_time_ms: None,
            details: Some(details),
        }
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ErrorDetail {
    pub field: String,
    pub message: String,
    pub code: Option<String>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct ValidationErrorResponse {
    pub success: bool,
    pub message: String,
    pub errors: Vec<ErrorDetail>,
    pub timestamp: DateTime<Utc>,
}

impl ValidationErrorResponse {
    pub fn new(message: &str, errors: Vec<ErrorDetail>) -> Self {
        Self {
            success: false,
            message: message.to_string(),
            errors,
            timestamp: Utc::now(),
        }
    }
}

pub fn validation_error(message: &str, errors: Vec<ErrorDetail>) -> Result<HttpResponse> {
    Ok(HttpResponse::BadRequest().json(ValidationErrorResponse::new(message, errors)))
}

// Utility macro for quick response creation
#[macro_export]
macro_rules! api_response {
    (ok, $data:expr) => {
        $crate::utils::response::ok($data)
    };
    (ok, $data:expr, $message:expr) => {
        $crate::utils::response::ok_with_message($data, $message)
    };
    (created, $data:expr) => {
        $crate::utils::response::created($data)
    };
    (created, $data:expr, $message:expr) => {
        $crate::utils::response::created_with_message($data, $message)
    };
    (no_content) => {
        $crate::utils::response::no_content()
    };
    (bad_request, $message:expr) => {
        $crate::utils::response::bad_request($message)
    };
    (unauthorized, $message:expr) => {
        $crate::utils::response::unauthorized($message)
    };
    (forbidden, $message:expr) => {
        $crate::utils::response::forbidden($message)
    };
    (not_found, $message:expr) => {
        $crate::utils::response::not_found($message)
    };
    (internal_error, $message:expr) => {
        $crate::utils::response::internal_error($message)
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_api_response_success() {
        let data = json!({"message": "test"});
        let response = ApiResponse::success(data.clone());
        
        assert!(response.success);
        assert_eq!(response.data, Some(data));
        assert!(response.message.is_none());
    }

    #[test]
    fn test_api_response_error() {
        let response = ApiResponse::<()>::error("Test error");
        
        assert!(!response.success);
        assert!(response.data.is_none());
        assert_eq!(response.message, Some("Test error".to_string()));
    }

    #[test]
    fn test_pagination_meta() {
        let meta = PaginationMeta::new(2, 10, 25);
        
        assert_eq!(meta.current_page, 2);
        assert_eq!(meta.per_page, 10);
        assert_eq!(meta.total_items, 25);
        assert_eq!(meta.total_pages, 3);
        assert!(meta.has_next);
        assert!(meta.has_prev);
    }

    #[test]
    fn test_paginated_response() {
        let items = vec![1, 2, 3, 4, 5];
        let response = PaginatedResponse::new(items.clone(), 1, 5, 20);
        
        assert_eq!(response.items, items);
        assert_eq!(response.pagination.current_page, 1);
        assert_eq!(response.pagination.total_pages, 4);
        assert!(response.pagination.has_next);
        assert!(!response.pagination.has_prev);
    }

    #[test]
    fn test_service_health() {
        let healthy = ServiceHealth::healthy("database", 50);
        assert_eq!(healthy.name, "database");
        assert_eq!(healthy.status, "healthy");
        assert_eq!(healthy.response_time_ms, Some(50));
        
        let unhealthy = ServiceHealth::unhealthy("redis", json!({"error": "connection failed"}));
        assert_eq!(unhealthy.name, "redis");
        assert_eq!(unhealthy.status, "unhealthy");
        assert!(unhealthy.response_time_ms.is_none());
    }
}